﻿using DotNetCommon.Expressions;
using DotNetCommon.Expressions.Base;
using DotNetCommon.Expressions.Visit;
using DotNetCommon.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace DotNetCommon
{
    /// <summary>
    /// 表达式帮助类
    /// </summary>
    public static class ExpressionHelper
    {
        /// <summary>
        /// 返回lambda表达式树访问到第一个参数的属性名称数组，示例：
        /// <list type="bullet">
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => new{ p.Id,p.Name });// 输出: ["Id","Name"]
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => p.Id);// 输出: ["Id"]
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => p);// 输出: []
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessNames(p => $"id={p.Id},name={p.Name}");// 输出: ["Id","Name"]
        /// </code>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="lambda"></param>
        /// <returns></returns>
        public static List<string> GetAccessNames(LambdaExpression lambda) => GetAccessMutilNames(lambda).FirstOrDefault();

        /// <summary>
        /// 返回lambda表达式树访问到每个参数的属性名称数组，示例：
        /// <list type="bullet">
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetAccessMutilNames((p1, p2) => new{ p1.Id,p2.Name });// 输出: [["Id"],["Name"]]
        /// </code>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="lambda"></param>
        /// <remarks>单个参数的输出示例，参考: <seealso cref="ExpressionHelper.GetAccessNames(LambdaExpression)"/></remarks>
        /// <returns></returns>
        public static List<List<string>> GetAccessMutilNames(LambdaExpression lambda)
        {
            if (lambda == null || lambda.Parameters.Count == 0) return new List<List<string>>(0);
            var paras = lambda.Parameters.ToList();
            var list = paras.Select(i => new List<string>()).ToList();

            var root = new ExpressionNode
            {
                Expression = lambda,
                _parameters = lambda.Parameters.ToList(),
                Children = new List<ExpressionNode>()
            };
            VisitTree(root);
            return list;

            //遍历
            void VisitTree(ExpressionNode node)
            {
                if (node.Expression == null) return;
                var visit = _caches[node.NodeType];
                visit.GetAccessName(node, VisitTree, paras, list);
            }
        }

        /// <summary>
        /// 获取初始化的属性名称，示例：
        /// <list type="bullet">
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(() => new{ Id=1,Name="小明" });// 输出: ["Id","Name"]
        /// </code>
        /// </item>
        /// <item>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(p => p.Id);// 输出: ["Id"]
        /// </code>
        /// <code>
        /// var cols = ExpressionHelper.GetInitOrReturnPropNames(p => p);// 输出: []
        /// </code>
        /// </item>
        /// </list>
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static List<string> GetInitOrReturnPropNames(LambdaExpression expression)
        {
            var names = new List<string>();
            if (expression.Body.NodeType == ExpressionType.MemberInit)
            {
                var memberInit = expression.Body as MemberInitExpression;
                var bindings = memberInit.Bindings;
                foreach (var item in bindings) names.Add(item.Member.Name);
            }
            //else if (expression.Body.NodeType == ExpressionType.Parameter)
            //{
            //    // p=>p
            //}
            else if (expression.Body.NodeType == ExpressionType.Convert)
            {
                // p=>(object)p.Id
                var unary = expression.Body as UnaryExpression;
                if (unary.Operand.NodeType == ExpressionType.MemberAccess)
                {
                    //p=>p.Id
                    names.Add((unary.Operand as MemberExpression).Member.Name);
                }
            }
            else if (expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                //p=>p.Id
                names.Add((expression.Body as MemberExpression).Member.Name);
            }
            return names;
        }

        /// <summary>
        /// 简化lambda表达式，示例：
        /// <code>
        /// //原表达式:
        /// var teachers = new List&lt;Teacher>;
        /// {
        ///     new Teacher{ Id=1,Name="孙悟空"},
        ///     new Teacher{ Id=2,Name="猪八戒"},
        ///     new Teacher{ Id=3,Name="沙僧"}
        ///  };
        ///  Expression&lt;Func&lt;Student, bool>> exp = p => teachers.FirstOrDefault(i => i.Name == "孙悟空").Id == p.TeacherId;
        ///  //进行简化
        ///  var newExp = ExpressionHelper.Reduce(exp);
        ///  //得到新的表达式: p => 1 == p.TeacherId;
        /// </code>
        /// </summary>
        /// <param name="lambdaExpression"></param>
        /// <remarks>
        /// 简化的原理: <br />
        /// <list type="bullet">
        /// <item>首先, 找到表达式中不引用参数的最长段,如示例的是: teachers.FirstOrDefault(i => i.Name == "孙悟空").Id;</item>
        /// <item>然后, 将其中的局部或其他静态变量提取成参数,重新生成一个lambda表达式, 如: (p) => p.FirstOrDefault(i => i.Name == "孙悟空").Id;</item>
        /// <item>然后, 将新生成的表达式编译并缓存结果,以便下次直接调用;</item>
        /// <item>最后, 重新拼接表达式树</item>
        /// </list>
        /// </remarks>
        /// <returns></returns>
        public static Expression ReduceLambda(LambdaExpression lambdaExpression)
        {
            var root = new ExpressionNode
            {
                Expression = lambdaExpression,
                _parameters = lambdaExpression.Parameters.ToList(),
                Children = new List<ExpressionNode>()
            };
            VisitTree(root);
            root.FullMarkString = $"({lambdaExpression.Parameters.Select(i => $"{i.Type.GetClassFullName()} {i.Name}").ToStringSeparated(",")})=>{root.Children[0].FullMarkString}";
            if (lambdaExpression.Parameters.Count == 0 || !root.Children[0].HasParameter)
            {
                var res = root.Children[0].Reduce();
                root.Expression = Expression.Constant(res);
                root.Children = new List<ExpressionNode>();
            }
            return root.Expression;

            //遍历,标记出来唯一值
            void VisitTree(ExpressionNode node)
            {
                if (node.Expression == null) return;
                var visit = _caches[node.NodeType];
                visit.Reduce(node, VisitTree);
            }
        }
        private static Dictionary<ExpressionType, BaseVisit> _caches = new Dictionary<ExpressionType, BaseVisit>()
        {
           #region 所有的节点类型
            { ExpressionType.Add, new AddVisit() },
            { ExpressionType.AddChecked, new AddCheckedVisit() },
            { ExpressionType.And, new AndVisit() },
            { ExpressionType.AndAlso, new AndAlsoVisit() },
            { ExpressionType.ArrayLength, new ArrayLengthVisit() },
            { ExpressionType.ArrayIndex, new ArrayIndexVisit() },
            { ExpressionType.Call, new CallVisit() },
            { ExpressionType.Coalesce, new CoalesceVisit() },
            { ExpressionType.Conditional, new ConditionalVisit() },
            { ExpressionType.Constant, new ConstantVisit() },
            { ExpressionType.Convert, new ConvertVisit() },
            { ExpressionType.ConvertChecked, new ConvertCheckedVisit() },
            { ExpressionType.Divide, new DivideVisit() },
            { ExpressionType.Equal, new EqualVisit() },
            { ExpressionType.ExclusiveOr, new ExclusiveOrVisit() },
            { ExpressionType.GreaterThan, new GreaterThanVisit() },
            { ExpressionType.GreaterThanOrEqual, new GreaterThanOrEqualVisit() },
            { ExpressionType.Invoke, new InvokeVisit() },
            { ExpressionType.Lambda, new LambdaVisit() },
            { ExpressionType.LeftShift, new LeftShiftVisit() },
            { ExpressionType.LessThan, new LessThanVisit() },
            { ExpressionType.LessThanOrEqual, new LessThanOrEqualVisit() },
            { ExpressionType.ListInit, new ListInitVisit() },
            { ExpressionType.MemberAccess, new MemberAccessVisit() },
            { ExpressionType.MemberInit, new MemberInitVisit() },
            { ExpressionType.Modulo, new ModuloVisit() },
            { ExpressionType.Multiply, new MultiplyVisit() },
            { ExpressionType.MultiplyChecked, new MultiplyCheckedVisit() },
            { ExpressionType.Negate, new NegateVisit() },
            { ExpressionType.UnaryPlus, new UnaryPlusVisit() },
            { ExpressionType.NegateChecked, new NegateCheckedVisit() },
            { ExpressionType.New, new NewVisit() },
            { ExpressionType.NewArrayInit, new NewArrayInitVisit() },
            { ExpressionType.NewArrayBounds, new NewArrayBoundsVisit() },
            { ExpressionType.Not, new NotVisit() },
            { ExpressionType.NotEqual, new NotEqualVisit() },
            { ExpressionType.Or, new OrVisit() },
            { ExpressionType.OrElse, new OrElseVisit() },
            { ExpressionType.Parameter, new ParameterVisit() },
            { ExpressionType.Power, new PowerVisit() },
            { ExpressionType.Quote, new QuoteVisit() },
            { ExpressionType.RightShift, new RightShiftVisit() },
            { ExpressionType.Subtract, new SubtractVisit() },
            { ExpressionType.SubtractChecked, new SubtractCheckedVisit() },
            { ExpressionType.TypeAs, new TypeAsVisit() },
            { ExpressionType.TypeIs, new TypeIsVisit() },
            { ExpressionType.Assign, new AssignVisit() },
            { ExpressionType.Block, new BlockVisit() },
            { ExpressionType.DebugInfo, new DebugInfoVisit() },
            { ExpressionType.Decrement, new DecrementVisit() },
            { ExpressionType.Dynamic, new DynamicVisit() },
            { ExpressionType.Default, new DefaultVisit() },
            { ExpressionType.Extension, new ExtensionVisit() },
            { ExpressionType.Goto, new GotoVisit() },
            { ExpressionType.Increment, new IncrementVisit() },
            { ExpressionType.Index, new IndexVisit() },
            { ExpressionType.Label, new LabelVisit() },
            { ExpressionType.RuntimeVariables, new RuntimeVariablesVisit() },
            { ExpressionType.Loop, new LoopVisit() },
            { ExpressionType.Switch, new SwitchVisit() },
            { ExpressionType.Throw, new ThrowVisit() },
            { ExpressionType.Try, new TryVisit() },
            { ExpressionType.Unbox, new UnboxVisit() },
            { ExpressionType.AddAssign, new AddAssignVisit() },
            { ExpressionType.AndAssign, new AndAssignVisit() },
            { ExpressionType.DivideAssign, new DivideAssignVisit() },
            { ExpressionType.ExclusiveOrAssign, new ExclusiveOrAssignVisit() },
            { ExpressionType.LeftShiftAssign, new LeftShiftAssignVisit() },
            { ExpressionType.ModuloAssign, new ModuloAssignVisit() },
            { ExpressionType.MultiplyAssign, new MultiplyAssignVisit() },
            { ExpressionType.OrAssign, new OrAssignVisit() },
            { ExpressionType.PowerAssign, new PowerAssignVisit() },
            { ExpressionType.RightShiftAssign, new RightShiftAssignVisit() },
            { ExpressionType.SubtractAssign, new SubtractAssignVisit() },
            { ExpressionType.AddAssignChecked, new AddAssignCheckedVisit() },
            { ExpressionType.MultiplyAssignChecked, new MultiplyAssignCheckedVisit() },
            { ExpressionType.SubtractAssignChecked, new SubtractAssignCheckedVisit() },
            { ExpressionType.PreIncrementAssign, new PreIncrementAssignVisit() },
            { ExpressionType.PreDecrementAssign, new PreDecrementAssignVisit() },
            { ExpressionType.PostIncrementAssign, new PostIncrementAssignVisit() },
            { ExpressionType.PostDecrementAssign, new PostDecrementAssignVisit() },
            { ExpressionType.TypeEqual, new TypeEqualVisit() },
            { ExpressionType.OnesComplement, new OnesComplementVisit() },
            { ExpressionType.IsTrue, new IsTrueVisit() },
            { ExpressionType.IsFalse, new IsFalseVisit() }, 
	#endregion
        };
    }
}
